﻿using System.Globalization;

namespace Sgr.BillNumbering.Services.Impl
{
    /// <summary>
    /// 将序列重置策略（SequenceResetStrategy）和时间点转换为时间键（TimeKey）。
    /// 时间键用于在数据库中隔离不同时间维度的序列计数器。
    /// </summary>
    public static class TimeKeyGenerator
    {
        /// <summary>
        /// 根据重置策略和当前 UTC 时间计算序列过期时间。
        /// </summary>
        /// <param name="strategy"></param>
        /// <param name="nowUtc"></param>
        /// <returns></returns>
        public static DateTime? CalculateExpiry(SequenceResetStrategy strategy, DateTime nowUtc) =>
             strategy switch
             {
                 SequenceResetStrategy.Never => null,
                 SequenceResetStrategy.Daily => nowUtc.Date.AddDays(1),
                 SequenceResetStrategy.Week => nowUtc.Date.AddDays(7),
                 SequenceResetStrategy.Monthly => new DateTime(nowUtc.Year, nowUtc.Month, 1, 0, 0, 0, DateTimeKind.Utc).AddMonths(1),
                 SequenceResetStrategy.Yearly => new DateTime(nowUtc.Year + 1, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                 _ => null
             };

        /// <summary>
        /// 根据重置策略和指定时间生成时间键。
        /// </summary>
        /// <param name="strategy">序列重置策略</param>
        /// <param name="dateTime">用于计算时间键的时间点（建议使用业务时区时间）</param>
        /// <returns>时间键字符串，如 "20251114"、"2025-W46"、"202511"、"2025"；Never 策略返回 "GLOBAL"</returns>
        public static string Generate(SequenceResetStrategy strategy, DateTime dateTime)
        {
            return strategy switch
            {
                SequenceResetStrategy.Never => "GLOBAL",
                SequenceResetStrategy.Daily => dateTime.ToString("yyyyMMdd"),
                SequenceResetStrategy.Week => GenerateIso8601WeekKey(dateTime),
                SequenceResetStrategy.Monthly => dateTime.ToString("yyyyMM"),
                SequenceResetStrategy.Yearly => dateTime.ToString("yyyy"),
                _ => throw new ArgumentOutOfRangeException(nameof(strategy), strategy, "Unsupported reset strategy.")
            };
        }

        /// <summary>
        /// 使用当前 UTC 时间生成时间键（仅用于调试或默认场景，生产环境建议传入明确时间）
        /// </summary>
        public static string Generate(SequenceResetStrategy strategy)
            => Generate(strategy, DateTime.UtcNow);

        /// <summary>
        /// 生成符合 ISO 8601 标准的周标识，格式：yyyy-'W'ww（例如：2025-W46）
        /// ISO 8601 规则：
        /// - 每周从周一 开始
        /// - 第1周是包含该年第一个周四的那一周
        /// - 年份可能与日历年的年份不同（如 2024-12-30 可能属于 2025-W01）
        /// </summary>
        private static string GenerateIso8601WeekKey(DateTime date)
        {
            // 使用 ISO 8601 周计算逻辑
            var isoCalendar = CultureInfo.InvariantCulture.Calendar;
            var dayOfWeek = isoCalendar.GetDayOfWeek(date);

            // 调整到最近的周四（ISO 规定：包含周四的周属于该年）
            // 如果是周五、六、日，则属于下周；周一二三属于本周
            var thursdayOffset = DayOfWeek.Thursday - dayOfWeek;
            if (thursdayOffset is > 3 or < -3)
            {
                thursdayOffset += dayOfWeek >= DayOfWeek.Friday ? 7 : -7;
            }
            var thursday = date.AddDays(thursdayOffset);

            var year = isoCalendar.GetYear(thursday);
            var week = isoCalendar.GetWeekOfYear(
                date,
                CalendarWeekRule.FirstFourDayWeek,
                DayOfWeek.Monday);

            // 补零到两位周数
            return $"{year}-W{week:D2}";
        }
    }
}